﻿// -----------------------------------------------------------------------
// <copyright file="HumanoServico.cs" company="Storytelling">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace Storytelling.Negocio
{
    using System;
    using System.Collections.Generic;
    using System.Data.Objects;
    using System.Linq;
    using System.Reflection;
    using Storytelling.Entidades;
    using Storytelling.Modelos;
    using Storytelling.Modelos.Utils;
    using Storytelling.Utils;
    using Storytelling.Negocio.Excecoes;

    public class HumanoServico : BaseServico<Humano, HumanoFormModel>
    {
        internal event Action<ObjectContext, Humano> OnCarregarPropriedades;
        internal event Action<HumanoFormModel> OnEspecificarTipoEntidade;
        internal event Action<ObjectContext, HumanoFormModel> OnPreOperacao;
        internal event Action<Type, Bolsa<bool>> OnBloquearOperacao;
        internal event Action<ObjectContext, Humano, TipoCampo, long, int> OnEvoluir;
        internal event Action<ObjectContext, Humano> OnGastarExperiencia;
        internal event Action<Type, Vantagem, Bolsa<bool>> OnPermitirVantagem;
        internal event Action<ObjectContext, Humano, Vantagem, Bolsa<bool>> OnPermitirVantagemDesconto;
        internal event Action<Humano> OnValidarUsoVantagens;
        internal event Action<ObjectContext, Humano, bool, int, IList<string>> OnGerenciarEnergia;

        internal HumanoServico()
        {

        }

        public override void Consultar(HumanoFormModel model, long id, Carga<Humano> carga)
        {
            base.Consultar(model, id, carga);

            if (OnEspecificarTipoEntidade != null)
            {
                OnEspecificarTipoEntidade(model);
            }
        }

        public override void Salvar(HumanoFormModel model)
        {
            if (!model.Entidade.Tamanho.HasValue)
            {
                model.Entidade.Tamanho = Tamanho.Adulto;
            }

            model.Entidade.Vitalidade = model.Entidade.Vigor + (int) model.Entidade.Tamanho.Value;
            model.Entidade.Vontade = model.Entidade.Perseveranca + model.Entidade.Autocontrole;

            Validar(model.Entidade);

            if (model.Entidade.Id == 0 && !GerenciadorAcesso.UsuarioCorrentePertence(Tipo.Narrador))
            {
                int[] atributos = new int[] 
                { 
                    model.Entidade.Forca + model.Entidade.Destreza + model.Entidade.Vigor +
                    (model.Entidade.Forca == 5 ? 1 : 0) + (model.Entidade.Destreza == 5 ? 1 : 0) + (model.Entidade.Vigor == 5 ? 1 : 0) - 3, 
                    model.Entidade.Presenca + model.Entidade.Manipulacao + model.Entidade.Autocontrole +
                    (model.Entidade.Presenca == 5 ? 1 : 0) + (model.Entidade.Manipulacao == 5 ? 1 : 0) + (model.Entidade.Autocontrole == 5 ? 1 : 0) - 3, 
                    model.Entidade.Inteligencia + model.Entidade.Raciocinio + model.Entidade.Perseveranca +
                    (model.Entidade.Inteligencia == 5 ? 1 : 0) + (model.Entidade.Raciocinio == 5 ? 1 : 0) + (model.Entidade.Perseveranca == 5 ? 1 : 0) - 3, 
                };

                atributos = atributos.OrderByDescending(i => i).ToArray();

                if (atributos[0] != 5 || atributos[1] != 4 || atributos[2] != 3)
                {
                    throw new StorytellingException(string.Format("A distribuição dos atributos do personagem não segue o padrão 5/4/3. Valores: {0}/{1}/{2}",
                        atributos[0], atributos[1], atributos[2]));
                }

                int[] habilidades = new int[] 
                { 
                    model.Entidade.Armamento.Value + model.Entidade.ArmasDeFogo.Value + model.Entidade.Briga.Value + model.Entidade.Conducao.Value +
                    model.Entidade.Dissimulacao.Value + model.Entidade.Esportes.Value + model.Entidade.Furto.Value + model.Entidade.Sobrevivencia.Value +
                    (model.Entidade.Armamento.Value == 5 ? 1 : 0) + (model.Entidade.ArmasDeFogo.Value == 5 ? 1 : 0) + (model.Entidade.Briga.Value == 5 ? 1 : 0) + 
                    (model.Entidade.Conducao.Value == 5 ? 1 : 0) + (model.Entidade.Dissimulacao.Value == 5 ? 1 : 0) + (model.Entidade.Esportes.Value == 5 ? 1 : 0) + 
                    (model.Entidade.Furto.Value == 5 ? 1 : 0) + (model.Entidade.Sobrevivencia.Value == 5 ? 1 : 0), 

                    model.Entidade.Astucia.Value + model.Entidade.Empatia.Value + model.Entidade.Expressao.Value + model.Entidade.Intimidacao.Value +
                    model.Entidade.Manha.Value + model.Entidade.Persuasao.Value + model.Entidade.Socializacao.Value + model.Entidade.TratoAnimais.Value +
                    (model.Entidade.Astucia.Value == 5 ? 1 : 0) + (model.Entidade.Empatia.Value == 5 ? 1 : 0) + (model.Entidade.Expressao.Value == 5 ? 1 : 0) + 
                    (model.Entidade.Intimidacao.Value == 5 ? 1 : 0) + (model.Entidade.Manha.Value == 5 ? 1 : 0) + (model.Entidade.Persuasao.Value == 5 ? 1 : 0) + 
                    (model.Entidade.Socializacao.Value == 5 ? 1 : 0) + (model.Entidade.TratoAnimais.Value == 5 ? 1 : 0), 

                    model.Entidade.Ciencias.Value + model.Entidade.Erudicao.Value + model.Entidade.Informatica.Value + model.Entidade.Investigacao.Value +
                    model.Entidade.Medicina.Value + model.Entidade.Ocultismo.Value + model.Entidade.Oficios.Value + model.Entidade.Politica.Value +
                    (model.Entidade.Ciencias.Value == 5 ? 1 : 0) + (model.Entidade.Erudicao.Value == 5 ? 1 : 0) + (model.Entidade.Informatica.Value == 5 ? 1 : 0) + 
                    (model.Entidade.Investigacao.Value == 5 ? 1 : 0) + (model.Entidade.Medicina.Value == 5 ? 1 : 0) + (model.Entidade.Ocultismo.Value == 5 ? 1 : 0) + 
                    (model.Entidade.Oficios.Value == 5 ? 1 : 0) + (model.Entidade.Politica.Value == 5 ? 1 : 0), 
                };

                habilidades = habilidades.OrderByDescending(i => i).ToArray();

                if (habilidades[0] != 11 || habilidades[1] != 7 || habilidades[2] != 4)
                {
                    throw new StorytellingException(string.Format("A distribuição dos habilidades do personagem não segue o padrão 11/7/4. Valores: {0}/{1}/{2}",
                        habilidades[0], habilidades[1], habilidades[2]));
                }

                if (model.Entidade.Especializacoes.Count != 3)
                {
                    throw new StorytellingException(string.Format("Um personagem inicial deve ter 3 especializações. Valor: {0}",
                        model.Entidade.Especializacoes.Count));
                }

                int vantagens = 0;

                foreach (var item in model.Entidade.Vantagens)
                {
                    if (item.Nivel == 5)
                    {
                        vantagens++;
                    }
                    vantagens += item.Nivel;
                }

                if (vantagens > 7)
                {
                    throw new StorytellingException(string.Format("Um personagem inicial não deve ter mais de 7 pontos em vantagens. Valor: {0}",
                        vantagens));
                }
            }

            base.Salvar(model);
        }

        protected override void PreOperacao(ObjectContext context, HumanoFormModel model)
        {
            if (model.Entidade.Id == 0)
            {
                model.Entidade.JogadorId = context.CreateObjectSet<Usuario>().Single(usu => usu.Login.Equals(model.Jogador)).Id;
            }
            else
            {
                Humano humano = context.CreateObjectSet<Humano>().Single(hum => hum.Id == model.Entidade.Id);
                model.Entidade.JogadorId = humano.Jogador.Id;
                model.Entidade.DanoAgravado = humano.DanoAgravado;
                model.Entidade.DanoLetal = humano.DanoLetal;
                model.Entidade.DanoContusao = humano.DanoContusao;
                model.Entidade.VontadeTemporaria = humano.VontadeTemporaria;
                model.Entidade.Experiencia = humano.Experiencia;
                model.Entidade.Energia = humano.Energia;
                model.Entidade.Fator = humano.Fator;
            }

            Type type = typeof(Humano);
            ObjectSet<Vantagem> objectSet = context.CreateObjectSet<Vantagem>();

            foreach (var item in model.Entidade.Vantagens)
            {
                Vantagem vantagem = objectSet.SingleOrDefault(ent => ent.Id == item.VantagemId);
                if (vantagem != null && vantagem.PreRequisitos.Any(ent => ((int)type.GetProperty(ent.Campo.Value.ToString())
                    .GetValue(model.Entidade, null)) < ent.Valor))
                {
                    throw new StorytellingException(string.Format("A vantagem '{0}' do personagem tem alguns pré-requisitos não atendidos", vantagem.Nome));
                }
            }

            if (OnPreOperacao != null)
            {
                OnPreOperacao(context, model);
            }
        }

        protected override string LoginUsuarioPermitido(Humano entidade)
        {
            return entidade != null ? entidade.Cronica.Narrador.Login : "";
        }

        public bool BloquearOperacaoExtensor(long id, string usuarioCorrente)
        {
            string login = null;
            Bolsa<bool> bolsa = new Bolsa<bool>();
            Type type = null;

            using (var context = Context)
            {
                Humano humano = context.CreateObjectSet<Humano>().SingleOrDefault(ent => ent.Id == id);
                login = LoginUsuarioPermitido(humano);
                type = humano.GetType();
            }

            if (string.IsNullOrEmpty(login))
            {
                return false;
            }

            if (OnBloquearOperacao != null)
            {
                OnBloquearOperacao(type, bolsa);
            }

            return !GerenciadorAcesso.UsuarioCorrentePertence(Tipo.Editor)
                && !usuarioCorrente.Equals(login) && bolsa.Objeto;
        }

        public bool BloquearJogador(long id, string usuarioCorrente)
        {
            string login = "";

            using (var context = Context)
            {
                login = context.CreateObjectSet<Humano>().SingleOrDefault(hum => hum.Id == id).Jogador.Login;
            }

            return !usuarioCorrente.Equals(login);
        }

        public virtual void Evoluir(EvolucaoModel model)
        {
            if (model.Campos != null)
            {
                using (var context = Context)
                {
                    ObjectSet<Vantagem> objectSetVantagem = context.CreateObjectSet<Vantagem>();

                    Humano humano = (Humano)context.ApplyOriginalValues("Humano",
                        context.GetObjectByKey(context.CreateEntityKey("Humano", new Humano { Id = model.Id.Value })));

                    Type type = humano.GetType();

                    if (type.BaseType == typeof(Humano))
                    {
                        if (!humano.Vantagens.All(hvn => hvn.Vantagem.UsadoPorHumano.Value))
                        {
                            throw new StorytellingException("Seu personagem tem vantagens que não podem ser usadas por um humano.");
                        }                     
                    }
                    else
                    {
                        if (OnValidarUsoVantagens != null)
                        {
                            OnValidarUsoVantagens(humano);
                        }
                    }

                    for (int i = 0; i < model.Campos.Length; i++)
                    {
                        string[] valores = model.Campos[i].Split(new char[] { '_' }, StringSplitOptions.RemoveEmptyEntries);

                        TipoCampo tipoCampo = (TipoCampo)Int32.Parse(valores[0]);
                        long valor = Int32.Parse(valores[1]);

                        switch (tipoCampo)
                        {
                            case TipoCampo.Atributo:
                            case TipoCampo.Habilidade:
                                int peso = tipoCampo == TipoCampo.Atributo ? 5 : 3;
                                PropertyInfo propertyInfo = typeof(Humano).GetProperty(((Campo)valor).ToString());
                                int nivel = (int)propertyInfo.GetValue(humano, null);

                                for (int x = 0; x < model.Incrementos[i]; x++)
                                {
                                    nivel++;
                                    humano.Experiencia -= nivel * peso;
                                }

                                propertyInfo.SetValue(humano, nivel, null);
                                break;
                            case TipoCampo.Especializacao:
                                humano.Experiencia -= 3;
                                humano.Especializacoes.Add(new HumanoEspecializacao()
                                {
                                    HumanoId = humano.Id,
                                    EspecializacaoId = valor
                                });
                                break;
                            case TipoCampo.Vantagem:
                                Vantagem vantagem = objectSetVantagem.SingleOrDefault(ent => ent.Id == valor);

                                Bolsa<bool> desconto = new Bolsa<bool>();

                                Bolsa<bool> bolsa = new Bolsa<bool>();

                                if (OnPermitirVantagem != null)
                                {
                                    OnPermitirVantagem(type, vantagem, bolsa);
                                }

                                if (OnPermitirVantagemDesconto != null)
                                {
                                    OnPermitirVantagemDesconto(context, humano, vantagem, desconto);
                                }

                                if (!(bolsa.Objeto || (vantagem.UsadoPorHumano.Value && type.BaseType == typeof(Humano))))
                                {
                                    throw new StorytellingException(string.Format("A vantagem '{0}' não estar habilitada para sua categoria.", 
                                        vantagem.Nome));
                                }

                                if (vantagem.DisponivelNaCriacao.Value)
                                {
                                    throw new StorytellingException(string.Format("Seu personagem não pode ter a vantagem '{0}', pois à mesma só estar disponível durante a criação.",
                                        vantagem.Nome));
                                }

                                if (!vantagem.PreRequisitos.All(ent => ((int)type.GetProperty(ent.Campo.Value.ToString())
                                    .GetValue(humano, null)) >= ent.Valor))
                                {
                                    throw new StorytellingException(string.Format("Seu personagem não atende os pré-requisitos da vantagem '{0}'.", 
                                        vantagem.Nome));
                                }

                                HumanoVantagem humanoVantagem = humano.Vantagens.SingleOrDefault(ent => ent.VantagemId == valor);

                                if (humanoVantagem == null)
                                {
                                    humanoVantagem = new HumanoVantagem();
                                    humanoVantagem.HumanoId = humano.Id;
                                    humanoVantagem.VantagemId = valor;
                                }

                                int[] niveis = vantagem.Niveis.OrderBy(ent => ent.Nivel).Select(ent => ent.Nivel).ToArray();

                                for (int x = 0; x < model.Incrementos[i]; x++)
                                {
                                    int nivelNovo = niveis.FirstOrDefault(niv => niv > humanoVantagem.Nivel);
                                    if (nivelNovo > 0)
                                    {
                                        for (int z = humanoVantagem.Nivel + 1; z <= nivelNovo; z++)
                                        {
                                            humano.Experiencia -= z * (desconto.Objeto ? 1 : 2);
                                        }
                                        humanoVantagem.Nivel = nivelNovo;
                                    }
                                }

                                if (humanoVantagem.Id == 0)
                                {
                                    context.AddObject("HumanoVantagem", humanoVantagem);
                                }
                                else
                                {
                                    context.ApplyCurrentValues("HumanoVantagem", humanoVantagem);
                                }
                                break;
                            case TipoCampo.Extensor:
                            default:
                                if (OnEvoluir != null)
                                {
                                    OnEvoluir(context, humano, tipoCampo, valor, model.Incrementos[i]);
                                }
                                break;
                        }
                    }

                    context.ApplyCurrentValues("Humano", humano);

                    Validar(humano);

                    if (humano.Experiencia >= 0)
                    {
                        humano.Vitalidade = humano.Vigor + (int)humano.Tamanho.Value;
                        humano.Vontade = humano.Perseveranca + humano.Autocontrole;

                        if (OnGastarExperiencia != null)
                        {
                            OnGastarExperiencia(context, humano);
                        }

                        context.SaveChanges();
                    }
                    else
                    {
                        throw new StorytellingException("Seu personagem não tem experiência suficiente.");
                    }
                } 
            }
        }

        public IList<KeyValuePair<string, IEnumerable<string>>> GerenciarStatus(StatusModel model)
        {
            IList<KeyValuePair<string, IEnumerable<string>>> lista = new List<KeyValuePair<string, IEnumerable<string>>>();

            using (var context = Context)
            {
                ObjectSet<Humano> objectSet = context.CreateObjectSet<Humano>();

                for (int i = 0; i < model.Personagem.Length; i++)
                {
                    if (model.EnergiaValor[i] > 0 || model.ExperienciaValor[i] > 0 || model.VitalidadeValor[i] > 0 || model.VontadeValor[i] > 0 || model.VitalidadeBonus[i] == 0)
                    {
                        long humanoId = model.Personagem[i];
                        Humano humano = objectSet.SingleOrDefault(hum => hum.Id == humanoId);

                        IList<string> status = new List<string>();

                        if (model.VitalidadeValor[i] > 0 || model.VitalidadeBonus[i] == 0)
                        {
                            if (!humano.DanoContusao.HasValue)
                            {
                                humano.DanoContusao = 0;
                            }

                            if (!humano.DanoLetal.HasValue)
                            {
                                humano.DanoLetal = 0;
                            }

                            if (!humano.DanoAgravado.HasValue)
                            {
                                humano.DanoAgravado = 0;
                            }

                            TipoDano tipoDano = (TipoDano)model.TipoDano[i];
                            int vitalidade = humano.Vitalidade + model.VitalidadeBonus[i];

                            if (model.VitalidadeSinal[i])
                            {
                                bool extenderDanoLetal = humano.DanoContusao >= vitalidade;
                                bool extenderDanoAgravado = humano.DanoLetal >= vitalidade;
                                switch (tipoDano)
                                {
                                    case TipoDano.Contundente:
                                        humano.DanoContusao += model.VitalidadeValor[i];
                                        break;
                                    case TipoDano.Letal:
                                        humano.DanoContusao += model.VitalidadeValor[i];
                                        humano.DanoLetal += model.VitalidadeValor[i];
                                        break;
                                    case TipoDano.Agravado:
                                        humano.DanoContusao += model.VitalidadeValor[i];
                                        humano.DanoLetal += model.VitalidadeValor[i];
                                        humano.DanoAgravado += model.VitalidadeValor[i];
                                        break;
                                    default:
                                        break;
                                }

                                if (humano.DanoContusao > vitalidade)
                                {
                                    if (extenderDanoLetal)
                                    {
                                        humano.DanoLetal += humano.DanoContusao - vitalidade; 
                                    }

                                    if (humano.DanoLetal > vitalidade)
                                    {
                                        if (extenderDanoAgravado)
                                        {
                                            humano.DanoAgravado += humano.DanoLetal - vitalidade; 
                                        }

                                        if (humano.DanoAgravado > vitalidade)
                                        {
                                            humano.DanoAgravado = vitalidade;
                                        }

                                        humano.DanoLetal = vitalidade;
                                    }

                                    humano.DanoContusao = vitalidade;
                                }
                            }
                            else if (model.VitalidadeValor[i] > 0)
                            {
                                switch (tipoDano)
                                {
                                    case TipoDano.Contundente:
                                        model.VitalidadeValor[i] = model.VitalidadeValor[i] < humano.DanoContusao
                                            ? model.VitalidadeValor[i] : humano.DanoContusao.Value;

                                        humano.DanoContusao -= model.VitalidadeValor[i];

                                        if (humano.DanoContusao < humano.DanoLetal)
                                        {
                                            humano.DanoContusao = humano.DanoLetal;
                                        }
                                        break;
                                    case TipoDano.Letal:
                                        model.VitalidadeValor[i] = model.VitalidadeValor[i] < humano.DanoLetal
                                            ? model.VitalidadeValor[i] : humano.DanoLetal.Value;

                                        humano.DanoContusao -= model.VitalidadeValor[i];
                                        humano.DanoLetal -= model.VitalidadeValor[i];

                                        if (humano.DanoLetal < humano.DanoAgravado)
                                        {
                                            humano.DanoContusao = humano.DanoAgravado;
                                            humano.DanoLetal = humano.DanoAgravado;
                                        }
                                        break;
                                    case TipoDano.Agravado:
                                        model.VitalidadeValor[i] = model.VitalidadeValor[i] < humano.DanoAgravado 
                                            ? model.VitalidadeValor[i] : humano.DanoAgravado.Value;

                                        humano.DanoContusao -= model.VitalidadeValor[i];
                                        humano.DanoLetal -= model.VitalidadeValor[i];
                                        humano.DanoAgravado -= model.VitalidadeValor[i];
                                        break;
                                    default:
                                        break;
                                }
                            }

                            humano.Penalidade = (Penalidade)Math.Abs(
                                vitalidade - humano.DanoContusao < 3 ? vitalidade - humano.DanoContusao.Value - 3 : 0);

                            status.Add(string.Format("Vitalidade = {0}{1} -> [Agravado] = {2} | [Letal] = {3} | [Contusão] = {4} | [Penalidade] = {5}",
                                humano.Vitalidade, model.VitalidadeBonus[i] > 0 ? string.Format("+{0}", model.VitalidadeBonus[i]) : "",
                                humano.DanoAgravado, humano.DanoLetal - humano.DanoAgravado, humano.DanoContusao - humano.DanoLetal, (int)humano.Penalidade));
                        }

                        if (model.VontadeValor[i] > 0)
                        {
                            if (!humano.VontadeTemporaria.HasValue)
                            {
                                humano.VontadeTemporaria = 0;
                            }

                            if (model.VontadeSinal[i])
                            {
                                humano.VontadeTemporaria += model.VontadeValor[i];

                                if (humano.VontadeTemporaria > humano.Vontade)
                                {
                                    humano.VontadeTemporaria = humano.Vontade;
                                }
                            }
                            else
                            {
                                humano.VontadeTemporaria -= model.VontadeValor[i];

                                if (humano.VontadeTemporaria < 0)
                                {
                                    humano.VontadeTemporaria = 0;
                                }
                            }

                            status.Add(string.Format("Força de Vontade = {0} / {1}", humano.VontadeTemporaria, humano.Vontade));
                        }

                        if (model.EnergiaValor[i] > 0 && OnGerenciarEnergia != null)
                        {
                            OnGerenciarEnergia(context, humano, model.EnergiaSinal[i], model.EnergiaValor[i], status);
                        }

                        if (model.ExperienciaValor[i] > 0)
                        {
                            if (model.ExperienciaSinal[i])
                            {
                                humano.Experiencia += model.ExperienciaValor[i];
                                status.Add("Ganhou experiência!");
                            }
                            else
                            {
                                humano.Experiencia -= model.ExperienciaValor[i];
                                status.Add("Perdeu experiência!");
                            }
                        }

                        lista.Add(new KeyValuePair<string, IEnumerable<string>>(string.Format("{0} ({1})", 
                            humano.Nome, humano.Jogador.Nome), status));

                        context.ApplyCurrentValues("Humano", humano);
                    }
                }
                context.SaveChanges();
            }

            return lista;
        }

        private void Validar(Humano entidade)
        {
            int limite = entidade.Fator < 6 ? 5 : entidade.Fator;

            bool isPassouLimite = 
            entidade.Forca > limite ||
            entidade.Destreza > limite ||
            entidade.Vigor > limite ||
            entidade.Presenca > limite ||
            entidade.Manipulacao > limite ||
            entidade.Autocontrole > limite ||
            entidade.Inteligencia > limite ||
            entidade.Raciocinio > limite ||
            entidade.Perseveranca > limite ||
            entidade.Armamento > limite ||
            entidade.ArmasDeFogo > limite ||
            entidade.Briga > limite ||
            entidade.Conducao > limite ||
            entidade.Dissimulacao > limite ||
            entidade.Esportes > limite ||
            entidade.Furto > limite ||
            entidade.Sobrevivencia > limite ||
            entidade.Astucia > limite ||
            entidade.Empatia > limite ||
            entidade.Expressao > limite ||
            entidade.Intimidacao > limite ||
            entidade.Manha > limite ||
            entidade.Persuasao > limite ||
            entidade.Socializacao > limite ||
            entidade.TratoAnimais > limite ||
            entidade.Ciencias > limite ||
            entidade.Erudicao > limite ||
            entidade.Informatica > limite ||
            entidade.Investigacao > limite ||
            entidade.Medicina > limite ||
            entidade.Ocultismo > limite ||
            entidade.Oficios > limite ||
            entidade.Politica > limite;

            if (isPassouLimite)
            {
                throw new StorytellingException(string.Format("As caracteristicas do personagem não deve ter mais {0} pontos", limite));
            }
        }

        public Humano Visualizar(long id, Carga<Humano> carga)
        {
            using (var context = Context)
            {
                ObjectQuery<Humano> objectQuery = context.CreateObjectSet<Humano>();

                foreach (var item in carga)
                {
                    objectQuery = objectQuery.Include(item);
                }

                Humano humano = objectQuery.SingleOrDefault(ent => ent.Id == id);

                if (OnCarregarPropriedades != null && humano.GetType().BaseType != typeof(Humano))
                {
                    OnCarregarPropriedades(context, humano);
                }

                return humano;
            }
        }

        public KeyValuePair<int, bool> PegarProximoNivelDescontoVantagem(long id, long idVantagem, int nivel)
        {
            using (var context = Context)
            {
                Bolsa<bool> desconto = new Bolsa<bool>();

                VantagemNivel vantagemNivel = context.CreateObjectSet<VantagemNivel>()
                    .Where(vni => vni.VantagemId == idVantagem && vni.Nivel > nivel).OrderBy(vni => vni.Nivel).FirstOrDefault();

                if (vantagemNivel != null)
                {
                    nivel = vantagemNivel.Nivel;

                    if (OnPermitirVantagemDesconto != null)
                    {
                        OnPermitirVantagemDesconto(context, context.CreateObjectSet<Humano>().SingleOrDefault(ent => ent.Id == id),
                            vantagemNivel.Vantagem, desconto);
                    }
                }

                return new KeyValuePair<int, bool>(nivel, desconto.Objeto);
            }
        }
    }
}
